import json, io, random, string

"""
这是一个webdog mapeditor3地图编辑器内部数据格式转换为geojson格式的工具
"""


class Webdog:
    """
    weight和dash_array参数用于还原图层样式中的dashArray设置
    因为mapeditor3中，dashArray参数是根据wight进行动态计算的
    所以在存储配置时，只保存了dashArray的类型编号，而不是真正的array
    因此需要进行转换
    """
    STYLES = {
        'color': "#1E90FF",
        'markerColor': "#000000",
        'opacity': 1,
        'fillColor': "#1E90FF",
        'fillOpacity': 0.4,
        'weight': 2,
        "dashArray": 1,
        "pattern": 1,
        "icon": 'text',
        'marker': 'text',
        'size': 'auto',
        'font': 1,
        'arrow': 0,
        'smooth': 0
    }
    WEIGHT = 2
    DASH_ARRAY = {
        1: None,
        2: lambda w: [w * 2],
        3: lambda w: [w * 3, w * 2, 1, w * 2],
        4: lambda w: [w * 4, w * 2, 1, w * 2, 1, w * 2],
        5: {
            'lineCap': 'butt',
            'fn': lambda w: [1]
        },
        6: {
            'lineCap': 'butt',
            'fn': lambda w: [w, w]
        }
    }

    def __init__(self, map_data):
        self.map = None

        if isinstance(map_data, str):
            self.loads(map_data)
        elif isinstance(map_data, dict):
            self.map = map_data
        elif isinstance(map_data, io.IOBase):
            self.load(map_data)

    def _iter_layers(self, key=None):
        if not key:
            key = self.map['begin']

        layer = self.map['layers'][key]

        yield layer

        if 'children' in layer:
            for layer in self._iter_layers(layer['children']):
                yield layer

        if 'next' in layer:
            for layer in self._iter_layers(layer['next']):
                yield layer

    def _get_id(self, id_set):
        index = random.sample(string.ascii_letters + string.digits, 6)
        index = ''.join(index)

        if index in id_set:
            return self._get_id(id_set)
        else:
            id_set.add(index)
            return index

    def get_style(self, style):
        if 'dashArray' not in style or style['dashArray'] == 1:
            return style

        dash_array = self.DASH_ARRAY[style['dashArray']]
        weight = style.get('weight') or self.WEIGHT

        if isinstance(dash_array, dict):
            style['dashArray'] = dash_array.fn(weight)
            style['lineCap'] = dash_array['lineCap']
        else:
            style['dashArray'] = dash_array(weight)

        return style

    def is_multi_path(slef, coord):
        return isinstance(coord[0][0], list) and isinstance(coord[0][0][0], list)

    def reverse_coord(self, coord):
        if isinstance(coord[0], list):
            for array in coord:
                self.reverse_coord(array)
        else:
            coord.reverse()

        return coord

    def load(self, fp, *args, **kwargs):
        self.map = json.load(fp, *args, **kwargs)
        return self

    def loads(self, s, *args, **kwargs):
        self.map = json.loads(s, *args, **kwargs)
        return self

    def to_geojson(self, is_dumps=True):
        features = []

        for layer in self._iter_layers():
            layer_type = layer.get('type')

            if not layer_type:
                break

            coord = layer.get('coord')
            name = layer.get('name')
            note = layer.get('note')
            style = layer.get('style')
            properties = layer.get('properties') or {}
            feature_type = None

            if layer_type == "marker":
                if isinstance(coord[0], list):
                    feature_type = 'MultiPoint'
                else:
                    feature_type = 'Point'
            elif layer_type == 'polyline':
                if self.is_multi_path(coord):
                    feature_type = 'MultiLineString'
                else:
                    feature_type = 'LineString'
            elif layer_type == 'polygon':
                if self.is_multi_path(coord):
                    feature_type = 'MultiPolygon'
                else:
                    feature_type = 'Polygon'

            if name:
                properties['name'] = name

            if note:
                properties['note'] = note

            if style:
                properties.update(self.get_style(style))

            feature = {
                'type': 'Feature',
                'properties': properties,
                'geometry': {
                    'type': feature_type,
                    'coordinates': self.reverse_coord(coord)
                }
            }
            features.append(feature)

        geojson = {
            "type": "FeatureCollection",
            "features": features
        }

        if is_dumps:
            return json.dumps(geojson)
        else:
            return geojson

    def _get_layers(self, features):
        id_set = set()
        begin = None
        prev_layer = None
        layers = {}

        for feature in features:
            index = self._get_id(id_set)
            layer = {}
            layer_type = None
            feature_type = feature['geometry']['type']
            coord = feature['geometry']['coordinates']
            properties = feature.get('properties')

            if feature_type == 'Point' or feature_type == 'MultiPoint':
                layer_type = 'marker'
            elif feature_type == 'LineString' or feature_type == 'MultiLineString':
                layer_type = 'polyline'
            elif feature_type == 'Polygon' or feature_type == 'MultiPolygon':
                layer_type = 'polygon'

            for style in ['name', 'note'] + [p for p in self.STYLES.keys()]:
                if style in properties:
                    layer[style] = properties[style]
                    properties.pop(style)

            if properties:
                layer['properties'] = properties

            layer['type'] = layer_type
            layer['coord'] = self.reverse_coord(coord)

            if not begin:
                begin = index

            if prev_layer:
                prev_layer['next'] = index

            prev_layer = layer

            layers[index] = layer

        return [layers, begin]

    def to_webdog(self, is_dumps=True):
        layers, begin = self._get_layers(self.map['features'])
        webdog = {
            'title': "未命名地图",
            'zoom': 'auto',
            'minZoom': 3,
            'maxZoom': 18,
            'center': 'auto',
            'tileLayer': {
                'base': {
                    'name': 'qqTerrainSimple',
                    'opacity': 1,
                },
                'high': {
                    'name': None,
                    'opacity': 0.8,
                }
            },
            'showLevel': [
                {'min': 3, 'max': 18, 'size': 20},
                {'min': 5, 'max': 18, 'size': 18},
                {'min': 7, 'max': 18, 'size': 16},
                {'min': 9, 'max': 18, 'size': 14}
            ],
            'isSnap': True,
            'begin': begin,
            'layers': layers
        }

        if is_dumps:
            return json.dumps(webdog)
        else:
            return webdog


def webdog_to_geojson(data):
    return Webdog(data).to_geojson()


if __name__ == "__main__":
    from test_data import webdog_dict_data, webdog_json_data, geojson_data

    test1 = Webdog(webdog_dict_data).to_geojson()
    test2 = Webdog(webdog_json_data).to_geojson()
    test3 = Webdog(geojson_data).to_webdog()

    print(test1)
    print(test2)
    print(test3)
